#ifndef __watcher_kenny_dkfjiwoo3w3__
#define __watcher_kenny_dkfjiwoo3w3__

#include <tuple>
#include <map>
#include <list>
#include <string>
#include <memory>

#include <hude/utils/event_bus.hpp>
#include <hude/aio/context.hpp>

#include "settings.hpp"
#include "brick/brick.hpp"

namespace ha
{
    using any_t = hude::any_t;

    /*********************************************************************
     * @brief 全局运行配置
     */
    struct configs_t
    {
        std::string settings_;
        uint16_t    port_;
        size_t      debug_;
    };

    configs_t configs;

    /*********************************************************************
     * @brief 消息总线
     */
    using mbus_t = hude::utils::bus_t< mid_t, any_t >;

    /*********************************************************************
     * @brief 框架
     */
    class skeleton_t
    {
    public:
        /*********************************************************************
         * @brief 单元实例及集合类型
         */
        using element_t = std::tuple< mid_t, mid_t, pbrick_t, mbus_t::itr_cb_handle_t >;
        using unit_t    = std::tuple< mid_t, std::list< element_t > >;
        using units_t   = std::map< std::string, unit_t >;

        // index of element_t
        enum { emTX, emRX, emBRICK, emHANDLE };

        // index of unit_t
        enum { emMID, emELEMENT };

    public:
        skeleton_t() : aio_( ctx_t::aio() ) {}
        skeleton_t( aio_t aio ) : aio_( aio ) {}
        ~skeleton_t() { uninit(); }

        /*********************************************************************
         * @brief 框架初始化
         */
        bool init( const settings_t& );

        /*********************************************************************
         * @brief 框架反初始化
         */
        void uninit(); 

    private:
        bool load_units( const json_t& ); 
        bool load_element( const char*, const json_t&, std::list< element_t >& ); 

        bool mount( mid_t, element_t& );
        void unmount( element_t& );

        json_t get_args( const json_t& );

    private:
        units_t units_;
        mbus_t  bus_;
        aio_t   aio_;
    };

    /*********************************************************************
     * @brief 守护服务
     */
    class service_t
    {
    public:
        service_t() : aio_( ctx_t::aio() ), skeleton_( aio_ ) {}
        int run( configs_t& );

    private:
        aio_t      aio_;
        skeleton_t skeleton_;
    };

    /*********************************************************************
     * @brief 运行守护服务
     */
    int service_t::run( configs_t& cfg )
    {
        // llog_dug( "<app>", "settings: \n%s", settings.dump().c_str() );

        llog_nte( "<app>", "start service." );

        aio_->run( [=]{
            auto& settings = settings_t::global();

            auto ret = skeleton_.init( settings );

            if( ret )
            {
            }
        });

        llog_nte( "<app>", "service quit." );

        return 0;
    }

    /*********************************************************************
     * @brief 框架初始化
     */
    inline bool skeleton_t::init( const settings_t& settings )
    {
        auto factory = factory_brick_t::single();

        if( ! (*settings).is_object()                 ) return false;
        if( ! settings["bricks"].is_object()          ) return false;
        if( ! settings["units"].is_object()           ) return false;
        if( ! factory->init( settings["bricks"] )     ) return false;
        if( ! this->load_units( settings["units"] )        ) return false;

        return true;
    }

    /*********************************************************************
     * @brief 框架初始化
     */
    inline json_t skeleton_t::get_args( const json_t& e )
    {
        auto itre = e.find( "args" );

        if( itre == e.end() ) return json_t{};

        if( ! itre->is_string() ) return *itre;

        const auto& s = settings_t::global();
        auto itrc = s->find( "configs" );

        if( itrc == s->end() || ! itrc->is_object() ) return *itre;

        auto itra = itrc->find( itre->get< std::string >() );

        if( itra == itrc->end() ) return *itre;

        return *itra;
    }

    /*********************************************************************
     * @brief ...
     */
    inline bool skeleton_t::load_element( const char* unit, const json_t& elements, std::list< element_t >& _elements )
    {
        auto factory = factory_brick_t::single();

        for( auto e : elements )
        {
            if( ! e.is_object() || ! e["module"].is_string() )
            {
                llog_err( "<app>", "ERROR: Module invaild in parse unit %s", unit );

                return false;
            }

            auto peer = e["peer"];

            if( ! peer.is_object() || ! peer["tx"].is_number_unsigned() || ! peer["rx"].is_number_unsigned() )
            {
                llog_err( "<app>", "ERROR: Peer invaild in parse unit %s", unit );

                return false;
            }

            auto brick = factory->product( aio_, e["module"].get< std::string >(), get_args( e ) );

            if( ! brick )
            {
                llog_err( "<app>", "ERROR: instance module %s in parse unit %s",
                    e["module"].get< std::string >().c_str(), unit );

                return false;
            }

            _elements.emplace_back( peer["tx"].get< mid_t >(), peer["rx"].get< mid_t >(), brick, mbus_t::itr_cb_handle_t() );

            brick->loglevel( hude::log_debug_info_t::level() );

            // llog_dug( "<app>", "\t\t init element %s ok.", e["module"].get< std::string >().c_str() );
        }

        return true;
    }

    /*********************************************************************
     * @brief ...
     */
    inline bool skeleton_t::load_units( const json_t& config )
    {
        for( auto& item : config.items() )
        {
            auto u = item.value();

            if( ! u.is_object() || ! u["elements"].is_array() )
            {
                llog_err( "<app>", "ERROR: parse unit %s", item.key().c_str() );

                return false;
            }

            {
                auto enable = hude::utils::json_path_t::value< bool >( u, "enable" );

                if( enable && ! ( *enable ) )
                {
                    continue;
                }
            }

            if( u.find( "group" ) == u.end() )
            {
                llog_err( "<app>", "ERROR: Group invaild in parse unit %s", item.key().c_str() );

                return false;
            }

            auto g = u["group"];
            mid_t gid = 0;

            if( g.is_number_unsigned() )
            {
                gid = g.get< mid_t >();
            } else if( g.is_string() ) {
                gid = hude::from_string_t< mid_t >::get( g.get< std::string >() );
            } else {
                llog_err( "<app>", "ERROR: Group invaild in parse unit %s", item.key().c_str() );

                return false;
            }

            std::list< element_t > _elements;

            if( ! this->load_element( item.key().c_str(), u["elements"], _elements ) ) return false;

            auto ret = units_.emplace( item.key(), unit_t{} );

            if( ! ret.second )
            {
                llog_err( "<app>", "ERROR: unit %s existed.", item.key().c_str() );

                return false;
            }

            auto& unit = ret.first->second;
            std::get< emMID >( unit ) = gid;
            auto& element = std::get< emELEMENT >( unit );
            std::swap( element, _elements );

            for( auto itr = element.begin(); itr != element.end(); ++itr )
            {
                if( ! this->mount( gid, *itr ) )
                {
                    for( auto ritr = std::list< element_t >::reverse_iterator( itr ); ritr != element.rend(); ++ritr )
                    {
                        this->unmount( *ritr );
                    }

                    units_.erase( ret.first );

                    llog_err( "<app>", "ERROR: mount unit %s.", item.key().c_str() );

                    break;
                }
            }

            llog_nte( "<app>", "\t init unit %s done.", item.key().c_str() );
        }

        return true;
    }

    /*********************************************************************
     * @brief ...
     */
    inline void skeleton_t::uninit()
    {
        if( units_.empty() ) return;

        for( auto& u : units_ )
        {
            auto& es = std::get< emELEMENT >( u.second );
            for ( auto& e : es )
            {
                this->unmount( e );
            }
        }

        units_.clear();
    }

    /*********************************************************************
     * @brief ...
     */
    inline bool skeleton_t::mount( mid_t gid, element_t& e )
    {
        namespace pl = std::placeholders;

        // 消息总线的订阅事件处理函数，将事件数据转发给积木
        auto cb_rx = [&]( const mbus_t::id_t& mid, mbus_t::value_t v, bool& )
        {
            auto& buffer = hude::any_cast< hude::buffer_t >( v );

            std::get< emBRICK >( e )->send( &(buffer[0]), buffer.size() );

            llog_inf( "<app>", "<<< mbus<%x:%u>[ %s ]", mid >> 16U, mid & 0x0ffff, hude::utils::to_hex( buffer ).c_str() );
        };

        // 积木的接收事件处理函数，将事件数据转发到消息总线
        auto cb_tx = [&]( mid_t gid, const hude::buffer_t& buffer )
        {
            mid_t mid = gid | std::get< emTX >( e );

            this->bus_.event( mid, buffer );

            llog_tra( "<app>", ">>> mbus<%x:%u>[ %s ]", mid >> 16U, mid & 0x0ffff, hude::utils::to_hex( buffer ).c_str() );
        };

        gid = gid << 16;

        // 为积木订阅数据事件
        std::get< emHANDLE >( e ) = this->bus_.add_handle( gid | std::get< emRX >( e ), cb_rx );

        // 积木的原生文件描述符
        std::get< emBRICK >( e )->cb_recv( std::bind( cb_tx, gid, pl::_1 ) );

        return true;
    }

    /*********************************************************************
     * @brief ...
     */
    inline void skeleton_t::unmount( element_t& e )
    {
        this->bus_.del_handle( std::get< emRX >( e ), std::get< emHANDLE >( e ) );

        std::get< emBRICK >( e )->cb_recv();
    }

} //namespace ha

#endif //__watcher_kenny_dkfjiwoo3w3__
